import pandas as pd
from typing import Optional, Union, List
import logging
from pathlib import Path

class PandasVlookup:
    """Excel表格关联匹配合并工具类"""

    def __init__(self, config_path: Optional[str] = None):
        """
        初始化vlookup工具

        Args:
            config_path: 配置文件路径(可选)
        """
        self.logger = self._setup_logger()
        self.config = self._load_config(config_path) if config_path else {}

    def _setup_logger(self) -> logging.Logger:
        """设置日志"""
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.INFO)
        handler = logging.StreamHandler()
        handler.setFormatter(
            logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        )
        logger.addHandler(handler)
        return logger

    def _load_config(self, config_path: str) -> dict:
        """加载配置文件"""
        try:
            return pd.read_json(config_path).to_dict()
        except Exception as e:
            self.logger.error(f"加载配置文件失败: {str(e)}")
            return {}

    def read_excel(
        self,
        file_path: Union[str, Path],
        sheet_name: Optional[str] = None,
        **kwargs
    ) -> pd.DataFrame:
        """
        读取Excel文件

        Args:
            file_path: Excel文件路径
            sheet_name: 工作表名称
            **kwargs: 传递给pd.read_excel的其他参数

        Returns:
            DataFrame对象
        """
        try:
            return pd.read_excel(file_path, sheet_name=sheet_name, **kwargs)
        except Exception as e:
            self.logger.error(f"读取Excel文件失败: {str(e)}")
            raise

    def merge_exact(
        self,
        left_df: pd.DataFrame,
        right_df: pd.DataFrame,
        left_on: Union[str, List[str]],
        right_on: Union[str, List[str]],
        how: str = 'left',
        **kwargs
    ) -> pd.DataFrame:
        """
        精确匹配合并

        Args:
            left_df: 左侧DataFrame
            right_df: 右侧DataFrame
            left_on: 左侧关联键
            right_on: 右侧关联键
            how: 合并方式('left', 'right', 'outer', 'inner')
            **kwargs: 传递给pd.merge的其他参数

        Returns:
            合并后的DataFrame
        """
        try:
            return pd.merge(
                left_df,
                right_df,
                left_on=left_on,
                right_on=right_on,
                how=how,
                **kwargs
            )
        except Exception as e:
            self.logger.error(f"精确匹配合并失败: {str(e)}")
            raise

    def merge_fuzzy(
        self,
        left_df: pd.DataFrame,
        right_df: pd.DataFrame,
        left_on: str,
        right_on: str,
        tolerance: Optional[float] = None,
        direction: str = 'nearest',
        **kwargs
    ) -> pd.DataFrame:
        """
        模糊匹配合并

        Args:
            left_df: 左侧DataFrame
            right_df: 右侧DataFrame
            left_on: 左侧关联键
            right_on: 右侧关联键
            tolerance: 容差值
            direction: 匹配方向('backward', 'forward', 'nearest')
            **kwargs: 传递给merge_asof的其他参数

        Returns:
            合并后的DataFrame
        """
        try:
            # 确保数据已排序
            left_df = left_df.sort_values(left_on)
            right_df = right_df.sort_values(right_on)

            return pd.merge_asof(
                left_df,
                right_df,
                left_on=left_on,
                right_on=right_on,
                tolerance=tolerance,
                direction=direction,
                **kwargs
            )
        except Exception as e:
            self.logger.error(f"模糊匹配合并失败: {str(e)}")
            raise

    def save_excel(
        self,
        df: pd.DataFrame,
        output_path: Union[str, Path],
        sheet_name: str = 'Sheet1',
        **kwargs
    ) -> None:
        """
        保存为Excel文件

        Args:
            df: 要保存的DataFrame
            output_path: 输出文件路径
            sheet_name: 工作表名称
            **kwargs: 传递给to_excel的其他参数
        """
        try:
            df.to_excel(
                output_path,
                sheet_name=sheet_name,
                index=False,
                **kwargs
            )
            self.logger.info(f"已保存到: {output_path}")
        except Exception as e:
            self.logger.error(f"保存Excel文件失败: {str(e)}")
            raise
